# -*- coding: utf-8 -*-
"""
用户认证服务
"""
import jwt
from datetime import datetime, timedelta
from typing import Optional, Union
from sqlalchemy.orm import Session
from app.models.user import User
from app.config.database import get_db
from app.utils.response import Result

class AuthService:
    SECRET_KEY = "your-secret-key-here"  # 生产环境应该使用环境变量
    ALGORITHM = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES = 30

    @classmethod
    def authenticate_user(cls, db: Session, username: str, password: str) -> Optional[User]:
        """
        验证用户登录

        Args:
            db: 数据库会话
            username: 用户名或邮箱
            password: 密码

        Returns:
            User对象或None
        """
        # 尝试用用户名查找
        user = db.query(User).filter(User.username == username).first()

        # 如果用户名未找到，尝试用邮箱查找
        if not user:
            user = db.query(User).filter(User.email == username).first()

        # # 调试信息
        # if user:
        #     print(f"找到用户: {user.username}, 状态: {user.status}")
        #     print(f"密码哈希: {user.password_hash[:50]}...")  # 只打印前50个字符
        #     print(f"密码验证结果: {user.check_password(password)}")
        # else:
        #     print(f"未找到用户: {username}")

        # 验证用户是否存在且密码正确且状态启用
        if user and user.check_password(password) and user.status == 1:
            return user

        return None

    @classmethod
    def create_access_token(cls, data: dict, expires_delta: Optional[timedelta] = None) -> str:
        """
        创建访问令牌

        Args:
            data: 要编码的数据
            expires_delta: 过期时间间隔

        Returns:
            JWT令牌字符串
        """
        to_encode = data.copy()

        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(minutes=cls.ACCESS_TOKEN_EXPIRE_MINUTES)

        to_encode.update({"exp": expire})
        encoded_jwt = jwt.encode(to_encode, cls.SECRET_KEY, algorithm=cls.ALGORITHM)

        return encoded_jwt

    @classmethod
    def verify_token(cls, token: str) -> Optional[dict]:
        """
        验证令牌

        Args:
            token: JWT令牌

        Returns:
            解码后的数据或None
        """
        try:
            payload = jwt.decode(token, cls.SECRET_KEY, algorithms=[cls.ALGORITHM])
            return payload
        except jwt.PyJWTError:
            return None

    @classmethod
    def get_current_user(cls, db: Session, token: str) -> Optional[User]:
        """
        根据令牌获取当前用户

        Args:
            db: 数据库会话
            token: JWT令牌

        Returns:
            User对象或None
        """
        payload = cls.verify_token(token)
        if payload is None:
            return None

        user_id = payload.get("sub")
        if user_id is None:
            return None

        user = db.query(User).filter(User.id == user_id).first()
        return user

    @classmethod
    def login(cls, db: Session, username: str, password: str) -> Result:
        """
        用户登录
        目的：验证用户登录凭据并生成JWT令牌
        功能：验证用户身份、创建访问令牌、返回用户信息

        Args:
            db (Session): 数据库会话对象，用于执行数据库查询
            username (str): 用户名或邮箱，用于标识用户身份
            password (str): 明文密码，需要与数据库哈希值比较

        Returns:
            Result: 统一响应结果对象
                - 成功时包含access_token、token_type和用户信息
                - 失败时返回错误消息
        """
        # 验证用户身份，返回User对象或None
        user = cls.authenticate_user(db, username, password)

        if not user:
            # 登录失败，返回401错误
            return Result.login_failed("用户名或密码错误")

        # 创建访问令牌，包含用户ID和用户名
        access_token = cls.create_access_token(
            data={"sub": str(user.id), "username": user.username}
        )

        # 登录成功，返回令牌和用户信息
        return Result.success(
            data={
                "access_token": access_token,
                "token_type": "bearer",
                "expires_in": cls.ACCESS_TOKEN_EXPIRE_MINUTES * 60,  # 转换为秒：30分钟 = 1800秒
                "user": user.to_dict()
            },
            message="登录成功"
        )

    @classmethod
    def register(cls, db: Session, username: str, email: str, password: str,
                 nickname: str = None, phone: str = None) -> Result:
        """
        用户注册
        目的：创建新用户账号并自动登录
        功能：验证数据、检查重复、创建用户、生成令牌

        Args:
            db (Session): 数据库会话对象，用于执行数据库操作
            username (str): 用户名，必填，需唯一
            email (str): 邮箱地址，必填，需唯一
            password (str): 明文密码，必填，将被哈希加密存储
            nickname (str): 昵称，可选，默认为None
            phone (str): 手机号码，可选，默认为None

        Returns:
            Result: 统一响应结果对象
                - 成功时包含access_token、token_type和用户信息
                - 失败时返回错误消息（如用户名已存在、邮箱已注册等）
        """
        # 检查用户名是否已存在，确保用户名唯一性
        existing_user = db.query(User).filter(User.username == username).first()
        if existing_user:
            # 用户名已被占用，返回验证错误
            return Result.validation_error("用户名已存在")

        # 检查邮箱是否已注册，确保邮箱唯一性
        existing_email = db.query(User).filter(User.email == email).first()
        if existing_email:
            # 邮箱已被注册，返回验证错误
            return Result.validation_error("邮箱已被注册")

        # 创建新用户对象，初始状态为启用(status=1)
        new_user = User(
            username=username,  # 用户登录名
            email=email,  # 邮箱地址
            nickname=nickname if nickname else username,  # 昵称，默认使用用户名
            phone=phone,  # 手机号码
            status=1  # 用户状态：1-启用
        )

        # 设置密码哈希值，使用SHA256加密
        new_user.set_password(password)

        try:
            # 添加用户到数据库会话
            db.add(new_user)
            # 提交事务，将用户数据持久化到数据库
            db.commit()
            # 刷新对象，获取数据库自动生成的ID等字段
            db.refresh(new_user)

            # 创建访问令牌，用于自动登录
            access_token = cls.create_access_token(
                data={"sub": str(new_user.id), "username": new_user.username}
            )

            # 注册成功，返回令牌和用户信息
            return Result.success(
                data={
                    "access_token": access_token,  # JWT访问令牌
                    "token_type": "bearer",  # 令牌类型
                    "expires_in": cls.ACCESS_TOKEN_EXPIRE_MINUTES * 60,  # 转换为秒：30分钟 = 1800秒
                    "user": new_user.to_dict()  # 用户详细信息
                },
                message="注册成功"
            )

        except Exception as e:
            # 发生异常时回滚事务，避免脏数据
            db.rollback()
            # 返回服务器内部错误
            return Result.internal_error(f"注册失败：{str(e)}")

    @classmethod
    def update_profile(cls, db: Session, user_id: int, nickname: str = None,
                       bio: str = None, phone: str = None, avatar: str = None) -> Result:
        """
        更新用户资料
        目的：允许用户修改个人信息（昵称、简介、手机号、头像等）
        功能：验证数据、更新用户资料

        Args:
            db (Session): 数据库会话对象，用于执行数据库操作
            user_id (int): 用户ID，标识要更新的用户
            nickname (str): 昵称，可选，最多100字符
            bio (str): 个人简介，可选，文本类型
            phone (str): 手机号码，可选，最多20字符
            avatar (str): 头像文件路径，可选，最多255字符

        Returns:
            Result: 统一响应结果对象
                - 成功时包含更新后的用户信息
                - 失败时返回错误消息
        """
        # 查询用户是否存在
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            return Result.not_found("用户未找到")

        try:
            # 只更新提供了的字段（支持部分更新）
            # 如果传入了昵称参数，更新昵称
            if nickname is not None:
                if len(nickname) > 100:
                    return Result.validation_error("昵称长度不能超过100个字符")
                user.nickname = nickname

            # 如果传入了个人简介参数，更新简介
            if bio is not None:
                user.bio = bio

            # 如果传入了手机号参数，更新手机号
            if phone is not None:
                if len(phone) > 20:
                    return Result.validation_error("手机号长度不能超过20个字符")
                user.phone = phone

            # 如果传入了头像路径参数，更新头像（存储文件路径，不是URL）
            if avatar is not None:
                if len(avatar) > 255:
                    return Result.validation_error("头像路径长度不能超过255个字符")
                user.avatar = avatar

            # 提交数据库事务，保存更新
            db.commit()
            # 刷新对象，获取最新数据
            db.refresh(user)

            # 返回更新后的用户信息
            return Result.success(
                data=user.to_dict(),
                message="更新资料成功"
            )

        except Exception as e:
            # 发生异常时回滚事务
            db.rollback()
            return Result.internal_error(f"更新资料失败：{str(e)}")

    @classmethod
    def change_password(cls, db: Session, user_id: int, old_password: str, new_password: str) -> Result:
        """
        修改密码
        目的：允许用户修改登录密码
        功能：验证旧密码、检查新密码合法性、更新密码

        Args:
            db (Session): 数据库会话对象，用于执行数据库操作
            user_id (int): 用户ID，标识要修改密码的用户
            old_password (str): 旧密码（明文），用于验证用户身份
            new_password (str): 新密码（明文），将被加密存储

        Returns:
            Result: 统一响应结果对象
                - 成功时返回成功消息
                - 失败时返回错误消息（如旧密码错误、新密码不符合要求等）
        """
        # 查询用户是否存在
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            return Result.not_found("用户未找到")

        # 验证旧密码是否正确
        if not user.check_password(old_password):
            return Result.validation_error("旧密码错误")

        # 验证新密码长度（6-20字符）
        if len(new_password) < 6 or len(new_password) > 20:
            return Result.validation_error("新密码长度必须在6-20个字符之间")

        # 检查新密码是否与旧密码相同
        if old_password == new_password:
            return Result.validation_error("新密码不能与旧密码相同")

        try:
            # 设置新密码（自动进行SHA256加密）
            user.set_password(new_password)

            # 提交数据库事务，保存新密码
            db.commit()

            # 修改密码成功
            return Result.success(message="修改密码成功")

        except Exception as e:
            # 发生异常时回滚事务
            db.rollback()
            return Result.internal_error(f"修改密码失败：{str(e)}")